/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.sipper.filters;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import combi.sipper.io.InFlatFile;
import combi.utils.Couple;


import scoreBloc.DensiteCirculaire;
import scoreBloc.IntervalleMinimumCirculaire;
import scoreBloc.PoidsTotal;
import scoreBloc.Score;
import scoreBloc.Wd_barre;
import scoreBloc.genereAllBlocCaseSAT;


public class ExtractInfosResultatFiltrePlus {

	private static boolean supSeuil = true;
	private boolean infSeuil = false; //a faire
	private static boolean calculIntervalleMin = true;
	private static boolean bestSubSet = true; // Meilleur sous ensemble de kSIPs en accord avec la mesure
	private static int bestK = 0; // Nb de kSIPs à sélectionner; =< 0 indique la meilleur des toutes les possibilités.

	private static double seuil = 0.7; // seuil de selection des résultats.
	private static int k =10
	;

	//private static ModScore modScore= new LogMoins1surX(1);
	//private static String pathPrefix = "./ecocyc - ALL-CHORISMATE-PWY - 53/";
	//private static String pathPrefix = "./eco/";
	//private static String pathPrefix = "./eco00010/";
	//private static String pathPrefix = "./metabolisme_central/";
	//private static String pathPrefix = "./eco00910_shuffle/";
	//private static String pathPrefix = "./eco00010_shuffle_fenetre/";
	//private static String pathPrefix = "./eco00010_fenetre/";
	//private static String pathPrefix = "./metabolisme_central_shuffle/";
	//private static String pathPrefix = "./metabolisme_central_fenetre/";
	//private static String pathPrefix = "./metabolisme_central_shuffle_fenetre/";
	//private static String pathPrefix = "./eco00010_shuffle_meta/";
	//private static String pathPrefix = "./metabolisme_central_shuffle_meta/";
	//private static String pathPrefix = "./eco00010_shuffle_meta_fenetre/";
	//private static String pathPrefix = "./metabolisme_central_shuffle_meta_fenetre/";
	//private static String fullPathPrefix = pathPrefix;
	//private static String fullPathPrefix = "./eco/";
	//private static String fullPathPrefix = "./eco_shuffle/";
	//private static String fullPathPrefix = "./eco_shuffle_fenetre/";
	//private static String pathPrefix = System.getProperty("user.home") + "/Data/CoExp/eco_U00096_GDS2588/";
	//private static String pathPrefix = System.getProperty("user.home") + "/Data/CoExp/eco_U00096_GDS2588_distanceAlternative/";
	//private static String pathPrefix = System.getProperty("user.home") + "/Data/DistanceGenome/eco/";
	//private static String pathPrefix = System.getProperty("user.home") + "/Data/Chili/meta_reaction/";
	private static String pathPrefix = System.getProperty("user.home") + "/Data/Chili/Metacyc/ecoli/Gint_15.0/";
	//private static String pathPrefix = System.getProperty("user.home") + "/Data/Chili/Metacyc/afer243159/Gint/";




	//private static String ressources = System.getProperty("user.home") + "/Data/Chili/export/";
	private static String ressources = System.getProperty("user.home") + "/Data/Chili/Metacyc/ecoli/15.0/data/";
	//private static String ressources =  System.getProperty("user.home") + "/Data/Chili/Metacyc/afer243159/14.1/data/";
	//private static String pathPrefix = "./data/";
	public static void main(String[] args) {
		String outmodbestSubSet ="";
		if (bestSubSet)
		{
			outmodbestSubSet = "best_" + bestK + "_From_";
		}
		//String fileIn = pathPrefix + "cheminsTraduits.txt";
		//String fileIn = pathPrefix + "cheminsCouvrants_feuille_"+k+"ksp.txt";
		String fileIn = pathPrefix + "cheminsTraduits_10ksp.txt";
		//String fileIn = pathPrefix + "cheminsTraduits_"+k+"ksp.txt";
		//String fileIn = pathPrefix + "cheminsReduitsTraduits.txt";
		//String fileIn = pathPrefix + "cheminDecontract.txt";
		String fileOut1 = pathPrefix + "cheminsTraduitsFiltre_"+seuil+"_"+outmodbestSubSet+k+"ksp.txt";
		String fileOut = pathPrefix + "infoResultatFiltre"+ seuil+"_"+outmodbestSubSet+ k +"ksp.txt";
		//String fileOut = pathPrefix + "infoResultatCouvrants"+ k +"ksp.txt";
		//String fileOut = pathPrefix + "infoResultatReduits"+ k +"ksp.txt";
		String fileOutReactions = pathPrefix + "infoResultatReactionsFiltre"+ seuil+"_"+outmodbestSubSet+ k +"ksp.txt";
		//String fileOutReactions = pathPrefix + "infoResultatReduitsReactions"+ k +"ksp.txt";
		//String fileOutReactions = pathPrefix + "infoResultatCouvrantsReactions"+ k +"ksp.txt";
		String fileOutGenes = pathPrefix + "infoResultatGenesFiltre"+ seuil+"_"+outmodbestSubSet+ k +"ksp.txt";
		//String fileOutGenes = pathPrefix + "infoResultatReduitsGenes"+ k +"ksp.txt";
		//String fileOutGenes = pathPrefix + "infoResultatCouvrantsGenes"+ k +"ksp.txt";
		//String fileBase = ressources + "genesPos.txt";
		String fileBase = ressources + "genes.dat";
		//String fileBase = pathPrefix + "GDS2588_testM_moinslog.txt";
		//String fileBase = pathPrefix + "GDS2588_testM.txt";
		//String fileBase = fullPathPrefix + "GenePosShuffle.txt";


		Map<Integer, String> genome = new HashMap<Integer, String>();
		Map<String, Integer> genomeInv = new HashMap<String,Integer>();
		//Map<String, Integer> genomeDist = new HashMap<String,Integer>();
		//Map<Integer, Integer> equivGene = new HashMap<Integer, Integer>();
		loadGenome(fileBase, genome, genomeInv);
		//dist = loadMatrix(fileBase,genome, genomeInv);

		Score<String,String> scoreDensite = new DensiteCirculaire(genomeInv, 0.0);
		Score<String,String> scoreTailleIntervalle = new IntervalleMinimumCirculaire(genomeInv, Double.POSITIVE_INFINITY);
		Score<String,String> scorePoidsChemin = new PoidsTotal(genomeInv);

		try{
			int nb = 0;

			//chargement des �quivalences de clusters de genes pour la pvalue.
			BufferedReader in;
			String ch;

			in = new BufferedReader (new FileReader (fileIn));
			BufferedWriter out;
			BufferedWriter out1;
			BufferedWriter outR;
			BufferedWriter outG;
			out = new BufferedWriter(new FileWriter(fileOut));
			out1 = new BufferedWriter(new FileWriter(fileOut1));
			outR = new BufferedWriter(new FileWriter(fileOutReactions));
			outG = new BufferedWriter(new FileWriter(fileOutGenes));
			out.write("id poids nb_genes nb_reactions taille_intervalle pvalue\n");

			int limiteK = 0;
			int limiteMax = k;
			Double pvalue = null;
			//			System.out.println(dist.get(genomeInv.get("b4119"), genomeInv.get("b4119")));
			//			System.out.println(distance(genomeInv, dist,"b4119", "b4119"));
			//			System.exit(1);
			String bloc ="";
			//constitution d'un bloc à analyser!
			while ((ch = in.readLine()) != null)
			{
				try
				{
					//analyse du bloc!
					if (ch.isEmpty())
					{					
						System.err.println(nb +"e "+k+"-SIP");	
						nb = export(nb, bloc, genomeInv, scoreDensite, scorePoidsChemin, scoreTailleIntervalle, out, out1, outR, outG);
						limiteK = -1;
						bloc ="";
					}
					if (limiteK <= limiteMax)
					{
						bloc = bloc +"\n" + ch;
						limiteK++;
					}
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
				//enregistrement des valeurs extraitent dans structure.
			}
			if (ch!=null && !bloc.isEmpty())
				nb = export(nb, bloc, genomeInv, scoreDensite, scorePoidsChemin, scoreTailleIntervalle, out, out1, outR, outG);
			in.close();
			out.close();
			out1.close();
			outR.close();
			outG.close();


		}catch(Exception e){
			e.printStackTrace();
			System.exit(1);
		}
		finally
		{
		}
	}

	private static int export(int nb,
			String bloc,
			Map<String, Integer> genomeInv,
			Score<String,String> scoreDensite,
			Score<String,String> scorePoidsChemin,
			Score<String,String> scoreTailleIntervalle,
			BufferedWriter out,
			BufferedWriter out1,
			BufferedWriter outR,
			BufferedWriter outG) throws IOException
			{

		Set<String> reactionsTrouvees = null;
		Set<String> genesTrouves = null;
		Bloc<String,String> sip = str2Bloc(bloc);
		//verif genes
		reactionsTrouvees = sip.getReactionSet();
		boolean reactionsVide = reactionsTrouvees.isEmpty();
		genesTrouves = sip.getGeneSet();
		Set<String> test = new HashSet<String>(genesTrouves);
		boolean genesVide = test.isEmpty();
		test.removeAll(genomeInv.keySet());
		boolean genesValides = test.isEmpty();
		test.clear();

		if (!reactionsVide && genesValides && !genesVide)
		{
			// calcul du score du bloc
			nb++;
			Bloc<String,String> bestSip = calculScore(sip, scoreDensite, bestSubSet, bestK);
			//System.err.println("ch.isEmpty()");
			double poidsTot = scorePoidsChemin.getScore(bestSip);
			double tailleIntervalle = scoreTailleIntervalle.getScore(bestSip);
			//System.err.println(nb);
			if ((bestSip.getScore() >= seuil)|| !supSeuil)
			{
				//if (pvalue == null)
				{
					out.write(nb + " " + poidsTot + " " + genesTrouves.size() + " " + reactionsTrouvees.size() + " " + tailleIntervalle + " NA\n");
				}
				//else{
				//	out.write(nb + " " + poidsTot + " " + genesTrouves.size() + " " + reactionsTrouvees.size() + " " + tailleIntervalle + " " + pvalue + "\n");
				//}

				if (bestSubSet)
				{
					out1.write(Bloc2txt(bestSip, genomeInv));
				}
				else
				{
					out1.write(bloc);
					//out1.write(Bloc2txt(sip, genomeInv));
				}

				outR.write(nb + "\t");
				for(String r : reactionsTrouvees)
				{
					outR.write(r+" ");
				}
				outR.write("\n");

				outG.write(nb + "\t");
				for(String r : genesTrouves)
				{
					outG.write(r+" ");
				}
				outG.write("\n");
				bestSip.clear();
			}
		}
		//raz
		bloc ="";
		sip.clear();
		sip = null;
		reactionsTrouvees.clear();
		genesTrouves.clear();
		return nb;
			}

	private static String Bloc2txt(Bloc<String,String> bloc, Map<String, Integer> genomeInv)
	{
		Score<String,String> scoreW_d = new NeighbouringCoefficient(genomeInv, Double.POSITIVE_INFINITY);
		Score<String,String> scorePoidsChemin = new PoidsTotal(genomeInv);
		String txt = "";
		//System.err.println(bloc.toString());
		double sommePoids = 0;
		for (int i=0; i<bloc.getSize();i++)
		{
			Bloc<String,String> blocTemp = new Bloc<String,String>(bloc.getId());
			List<String> lr = bloc.getReactions(i);
			List<String> lg = bloc.getGenes(i);
			Set<String> sg = new HashSet<String>(lg);
			blocTemp.add(lr,lg);
			Double poids = scorePoidsChemin.getScore(blocTemp);
			txt = txt+ poids + "\t";
			txt = txt+ sg.size() + "\t";
			txt = txt+ poids/(double)sg.size() + "\t";
			sommePoids = sommePoids +poids/(double)sg.size();

			txt = txt + "[";
			for (int j = 0; j<lg.size(); j++)
			{
				txt = txt+ "("+lg.get(j)+":,"+lr.get(j)+"),";
			}
			txt = txt + "]\n";
			//blocTemp.clear();
		}
		String result = bloc.getId()+ "\t" + scoreW_d.getScore(bloc);
		//String result = bloc.getId()+ "\t" + sommePoids/bloc.getSize();
		result = result + "\n" + txt + "\n";
		System.err.println(bloc.getReactionSet().size()+"=" + bloc.getSize()+ "\n" + result);
		return result;
	}

	private static Bloc<String, String> calculScore(Bloc<String,String> sip, Score<String, String> scoreFct,
			boolean bestSubSet, int bestK) {
		// en fonction des paramètres subset et bestK, sélectionner le meilleur bloc.
		Bloc<String,String> result = sip;
		if (bestSubSet)
		{
			if (bestK >0)
			{
				//on selectionne le meilleur sous-ensemble de bestK chemin en accord avec le score
				genereAllBlocCaseSAT<String, String> subsets = new genereAllBlocCaseSAT<String, String>(sip);
				Bloc<String, String> temp = subsets.getFirst();
				temp.setScore(scoreFct.getScore(temp));
				result = temp; 
				while(subsets.hasNext())
				{
					temp = subsets.next();
					temp.setScore(scoreFct.getScore(temp));
					if (temp.getSize()< bestK &&
							((scoreFct.best(result.getScore(),temp.getScore())>0 ||
							(scoreFct.best(result.getScore(),temp.getScore())==0 && result.getSize() < temp.getSize())
							)))
					{
						result=new Bloc<String, String>(temp);
					}
					temp.clear();
				}
			}
			else
			{
				//on selectionne le meilleur sous-ensemble de k-SIPs possible
				genereAllBlocCaseSAT<String, String> subsets = new genereAllBlocCaseSAT<String, String>(sip);
				Bloc<String, String> temp = subsets.getFirst();
				temp.setScore(scoreFct.getScore(temp));
				result = temp; 
				while(subsets.hasNext())
				{
					temp = subsets.next();
					temp.setScore(scoreFct.getScore(temp));
					if (scoreFct.best(result.getScore(),temp.getScore())>0 ||
							(scoreFct.best(result.getScore(),temp.getScore())==0 && result.getSize() < temp.getSize()))
					{
						result=new Bloc<String, String>(temp);
					}
					temp.clear();
				}
			}
		}
		else{
			//on calcul juste le score du bloc
			sip.setScore(scoreFct.getScore(sip));
		}
		return result;
	}

	public static Bloc<String, String> str2Bloc(String s)
	{
		Matcher m;
		//String dd = "(?:[0-9]+(?:\\.[0-9]+)?)";
		String recoReaction = "(?:" +"[^ \t,\\]\\[)(]+" + ")";
		String beginBloc = "(?:(" + recoReaction + ")->(" + recoReaction + ")(?:\t(.+))?)";
		String equiv = "(?:\\[.+\\]\t(.+-" + recoReaction + "-" + recoReaction + ")\t(.+))";
		String equivBis = "(?:-(" + recoReaction + ")-(" + recoReaction + "))";
		String flottant = "(?:[0-9]+(?:\\.[0-9]+)?)";
		String scores = "(?:(" + flottant + ")\t(" + flottant + ")\t(" + flottant + ")\t" +"(.+)" + ")"; //gloutonnerie
		//String geneBloc = "(?:(" + flottant + ")\t(.+)\t(b[0-9]{4})"  + ")";
		String geneBloc = "(?:(" + "[^ \t,\\]\\[)(]+" + "))";
		String reaction = "(?:(" + "[^ \t,\\]\\[)(]+" + "))";
		//String geneName = "(?:(" + flottant + ")\t(" + flottant + ")\t(" + flottant + ")\t" +"\\[\\{\\((?:\\+|-)b[0-9]{4}:([^,:]+),.+:([^,:]+),R[0-9]{5}\\),?\\},?\\]" + ")"; //gloutonnerie
		//String geneName = "(?:"+ "(?:" + ":([^,]+),(" + recoReaction  + ")\\))" + ")"; //gloutonnerie limit�e
		String geneName = "(?:"+ "(?:" + "\\([+-]?([^:]+):([^,]*),(" + recoReaction  + ")\\))" + ")"; //gloutonnerie limit�e

		Pattern reco = Pattern.compile(beginBloc);
		Pattern recoGeneReaction = Pattern.compile(geneName);
		String id=null;
		Bloc<String, String> bloc = null;
		boolean newSIP = false;
		//System.err.println(s);
		for (String ch : s.split("\n"))
		{
			m = reco.matcher(ch);
			//reconnaissance des Rx -> Ry
			if (m.find())
			{
				id = m.group(1) + "->" + m.group(2);
				try{
					Double score = Double.valueOf(m.group(3));
					bloc = new Bloc<String, String>(id, score);
				}
				catch(Exception e)
				{
					e.printStackTrace();
					System.err.println("Création bloc sans score");
					bloc = new Bloc<String, String>(id);
				}
				//System.err.println(id);
			}

			m = recoGeneReaction.matcher(ch);
			List<String> genes = new LinkedList<String>();
			List<String> reactions = new LinkedList<String>();
			while(m.find())
			{
				genes.add(m.group(1));
				reactions.add(m.group(3));
				newSIP = true;
			}
			if (newSIP)
			{
				bloc.add(reactions, genes);
				newSIP = false;
			}
		}
		//System.err.println(bloc.toString());
		return bloc;
	}

	public static SortedSet<String> createOrderedGenome(Map<String, Map<String, Collection<String>>> genes, Collection<String> genesException)
	{
		SortedSet<String> genome = new TreeSet<String>(new CompareGeneEx<String>(genes, genesException));
		genome.addAll(genes.keySet());
		return genome;
	}
	public static class CompareGeneEx<T> implements Comparator<T>
	{

		private Map<String, Map<String, Collection<String>>> map;
		private Collection<T> genesException;
		private String rightPos = "RIGHT-END-POSITION";
		private String leftPos = "LEFT-END-POSITION";


		public CompareGeneEx(Map<String, Map<String, Collection<String>>> map, Collection<T> genesException)
		{
			this.map = map;
			this.genesException = genesException;
		}

		@Override
		public int compare(T o1, T o2) {
			if (genesException.contains(o1) || genesException.contains(o2))
			{
				return 1;
			}
			int result = Integer.parseInt(map.get(o1).get(leftPos).iterator().next().toString()) - Integer.parseInt(map.get(o2).get(leftPos).iterator().next().toString());
			if (result == 0)
			{
				result = Integer.parseInt(map.get(o1).get(rightPos).iterator().next().toString()) - Integer.parseInt(map.get(o2).get(rightPos).iterator().next().toString());
			}
			return result;
		}

	}
	private static void loadGenome(String fileIn, Map<Integer, String> genome,
			Map<String, Integer> genomeInv) {
		Map<String, Map<String, Collection<String>>> genes = new HashMap<String, Map<String, Collection<String>>>();
		InFlatFile genesIn = new InFlatFile(fileIn);
		try {
			genes = genesIn.readFile();
			Collection<String> distanceGeneException = new HashSet<String>();
			String rightPos = "RIGHT-END-POSITION";
			String leftPos = "LEFT-END-POSITION";
			String geneProduct = "PRODUCT";

			Set<String> typesGenes = new HashSet<String>();
			for (Entry<String, Map<String, Collection<String>>> entry : genes.entrySet())
			{
				//gene sans position sur le genome
				if (!entry.getValue().containsKey(leftPos) || !entry.getValue().containsKey(rightPos))
				{
					distanceGeneException.add(entry.getKey());
				}
			}
			SortedSet<String> genePos = createOrderedGenome(genes, distanceGeneException);

			//String recoGene = "(?:" + "([^:]+):(.+)" + ")";
			int pos = 0;
			for(String s : genePos)
			{
				genome.put(pos, s);
				genomeInv.put(s, pos);
				pos++;
			}		
			System.out.println("Taille genome : " + genome.size() + "," + genomeInv.size());
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}


	private static double distance(Map<String, Integer> genome, String g1, String g2)
	{
		int taille = genome.size();
		int result = Math.abs(genome.get(g1) - genome.get(g2));
		if (taille < 2*result)
		{
			return taille - result;
		}
		return result;
	}

	//	private static String bijString(List<Integer> set)
	//	{
	//		String result = "[";
	//		for (Integer i : set)
	//		{
	//			/*result = result + ("(" + bijection.get(i).getFirst().getFirst()
	//						+ "," + bijection.get(i).getSecond() + "),");*/
	//			result = result + ("(" + bijection.get(i).getFirst().getFirst() + ":" + bijection.get(i).getFirst().getSecond()
	//					+ "," + bijection.get(i).getSecond() + "),");
	//		}
	//		result = result + ("]");
	//		//System.out.println(result);
	//		return result;
	//	}

	private static int intervalleMin(Map<String, Integer> genome, Collection<String> genes)
	{
		List<String> l = new ArrayList<String>(genes);
		Collections.sort(l, new CompareGene(genome));
		String pred = null;
		int max = 0;
		for (String g : l)
		{
			if (pred != null)
			{
				int score = genome.get(g) - genome.get(pred);
				if (score > max)
				{
					max = score;
				}
			}
			pred = g;
		}
		if (!l.isEmpty())
		{
			int score = genome.size() - (genome.get(l.get(l.size()-1)) - genome.get(l.get(0)));
			if (score > max)
			{
				max = score;
			}
		}
		max = genome.size() - max;
		return max + 1;
	}

	private static class CompareGene implements Comparator<String>
	{
		Map<String, Integer> genome;

		public CompareGene(Map<String, Integer> genome)
		{
			this.genome = genome;
		}

		@Override
		public int compare(String o1, String o2) {
			// TODO Auto-generated method stub
			return genome.get(o1).compareTo(genome.get(o2));
		}

	}



	private static Double pvalueMin(String fileIn)
	{
		Double result = Double.POSITIVE_INFINITY;
		//tenir compte de P, C et F ?
		try 
		{
			String ch;
			BufferedReader in = new BufferedReader (new FileReader (fileIn));
			Matcher m;
			String tag = "^CORRECTED P-VALUE\t(.+)$";
			Pattern reco = Pattern.compile(tag);
			while ((ch = in.readLine()) != null)
			{
				//reconnaissance
				m = reco.matcher(ch);
				while (m.find())
				{
					Double candidat = Double.valueOf(m.group(1));
					if (candidat<result)
					{
						result = candidat;
					}
				}
			}
		} 
		catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (result.isInfinite())
			result = null;
		return result;
	}

	private static String printList (List<String> l)
	{
		String result = new String();
		for (String ch : l)
		{
			result = result + ch + " ";
		}
		return result;
	}


}
